//
//  LXCodableWrapper.swift
//  LXReuseTools
//
//  Created by yoctech on 2021/10/27.
//

import Foundation

public protocol LXDefaultValue {
    associatedtype Value: Decodable
    static var defaultValue: Value { get }
}

@propertyWrapper
public struct LXDefault<T: LXDefaultValue> {
    public var wrappedValue: T.Value
    
    public init(wrappedValue: T.Value) {
        self.wrappedValue = wrappedValue
    }
}

extension LXDefault: Decodable {
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let value = try? container.decode(T.Value.self) {
            wrappedValue = value
        }
        else {
            if let value = try? container.decode(String.self) {
                if T.Value.self == Int.self {
                    wrappedValue = Int(value) as! T.Value
                }
                else if T.Value.self == Float.self || T.Value.self == CGFloat.self {
                    wrappedValue = Float(value) as! T.Value
                }
                else if T.Value.self == Double.self {
                    wrappedValue = Double(value) as! T.Value
                }
                else if T.Value.self == Bool.self {
                    wrappedValue = (value.lowercased() == "1" || value.lowercased() == "yes" || value.lowercased() == "true") as! T.Value
                }
                else {
                    wrappedValue = T.defaultValue
                }
            }
            else if let value = try? container.decode(Int.self) {
                if T.Value.self == String.self {
                    wrappedValue = "\(value)" as! T.Value
                }
                else if T.Value.self == Float.self || T.Value.self == CGFloat.self {
                    wrappedValue = Float(value) as! T.Value
                }
                else if T.Value.self == Double.self {
                    wrappedValue = Double(value) as! T.Value
                }
                else if T.Value.self == Bool.self {
                    wrappedValue = (value == 1) as! T.Value
                }
                else {
                    wrappedValue = T.defaultValue
                }
            }
            else if let value = try? container.decode(Double.self) {
                if T.Value.self == String.self {
                    wrappedValue = "\(value)" as! T.Value
                }
                else if T.Value.self == Float.self || T.Value.self == CGFloat.self {
                    wrappedValue = Float(value) as! T.Value
                }
                else if T.Value.self == Int.self {
                    wrappedValue = Int(value) as! T.Value
                }
                else if T.Value.self == Bool.self {
                    wrappedValue = (value == 1) as! T.Value
                }
                else {
                    wrappedValue = T.defaultValue
                }
            }
            else if let value = try? container.decode(Bool.self) {
                if T.Value.self == String.self {
                    wrappedValue = "\(value)" as! T.Value
                }
                else {
                    let aValue = value ? 1 : 0
                    wrappedValue = (aValue as? T.Value) ?? T.defaultValue
                }
            }

            else {
                wrappedValue = T.defaultValue
            }
        }
    }
}

extension KeyedDecodingContainer {
    public func decode<T>(
        _ type: LXDefault<T>.Type,
        forKey key: Key
    ) throws -> LXDefault<T> where T: LXDefaultValue {
        try decodeIfPresent(type, forKey: key) ?? LXDefault(wrappedValue: T.defaultValue)
    }
    
    
}


//MARK: - Bool

public struct BoolFalse: LXDefaultValue {
    public static let defaultValue = false
}
public struct BoolTrue: LXDefaultValue {
    public static let defaultValue = true
}

//MARK: - String

public struct StringEmpty: LXDefaultValue {
    public static let defaultValue = ""
}
public struct StringUnknown: LXDefaultValue {
    public static let defaultValue = "unknown"
}
public struct StringUnnamed: LXDefaultValue {
    public static let defaultValue = "unnamed"
}
public struct StringUnvalued: LXDefaultValue {
    public static let defaultValue = "--"
}

//MARK: - Int

public struct IntZero: LXDefaultValue {
    public static let defaultValue = 0
}
public struct IntNegativeOne: LXDefaultValue {
    public static let defaultValue = -1
}
public struct IntOne: LXDefaultValue {
    public static let defaultValue = 1
}

//MARK: - Float

public struct FloatZero: LXDefaultValue {
    public static let defaultValue = 0
}
public struct FloatNegativeOne: LXDefaultValue {
    public static let defaultValue = -1
}
public struct FloatOne: LXDefaultValue {
    public static let defaultValue = 1
}

//MARK: - Double

public struct DoubleZero: LXDefaultValue {
    public static let defaultValue = 0
}
public struct DoubleNegativeOne: LXDefaultValue {
    public static let defaultValue = -1
}
public struct DoubleOne: LXDefaultValue {
    public static let defaultValue = 1
}
